package com.aizuda.easyManagerTool.service.terminal.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.aizuda.easy.security.domain.Rep;
import com.aizuda.easy.security.domain.Req;
import com.aizuda.easy.security.exp.impl.BasicException;
import com.aizuda.easy.security.server.EasySecurityServer;
import com.aizuda.easyManagerTool.domain.bo.PackagesBO;
import com.aizuda.easyManagerTool.domain.dto.socket.SocketMessageDTO;
import com.aizuda.easyManagerTool.domain.dto.terminal.*;
import com.aizuda.easyManagerTool.domain.vo.server.ServerCompleteVO;
import com.aizuda.easyManagerTool.domain.vo.setting.SettingUserVO;
import com.aizuda.easyManagerTool.domain.vo.socket.SocketMessageVO;
import com.aizuda.easyManagerTool.mapper.server.ServerMapper;
import com.aizuda.easyManagerTool.service.setting.PackageCheckService;
import com.aizuda.easyManagerTool.service.socket.SocketEventService;
import com.aizuda.easyManagerTool.service.socket.impl.SocketSessionManager;
import com.aizuda.easyManagerTool.service.terminal.RemotePartService;
import com.aizuda.easyManagerTool.util.SSHManagerUtil;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service("remotePart")
public class RemotePartServiceImpl extends AbstractSSHTemplate implements RemotePartService, SocketEventService, PackageCheckService {

    @Resource
    ServerMapper serverMapper;
    @Resource
    EasySecurityServer easySecurityServer;
    Map<String,TerminalSessionManager.Manager> map = new ConcurrentHashMap<>();

    @Override
    public void onMessage(String token, WebSocketSession session, SocketMessageDTO message) throws IOException {
        SSHWindowsDTO window = message.getWindow();
        JSONObject jsonObject = (JSONObject) message.getJson();
        RemotePartMsgDTO remotePartMsgDTO = jsonObject.toBean(new TypeReference<RemotePartMsgDTO>() {});
        map.keySet().forEach(item -> {
            if (!item.startsWith(remotePartMsgDTO.getToken())) {
                return;
            }
            TerminalSessionManager.Manager manager = map.get(item);
            OutputStream outputStream = null;
            try {
                String id = item.substring(item.lastIndexOf(":")+1);
                ChannelShell channelShell = SSHManagerUtil.setChannelShell(manager.getSession(), manager, null);
                if(ObjectUtil.isNotEmpty(window) && Integer.valueOf(id).equals(window.getId())) {
                    channelShell.setPtySize(window.getClos(), window.getRows(), window.getWidth(), window.getHeight());
                }
                if (StrUtil.isNotEmpty(remotePartMsgDTO.getCommand())) {
                    setCommand(channelShell,remotePartMsgDTO.getCommand());
                }
            }catch(Exception e){
                close(manager);
                if(outputStream != null){
                    try {
                        outputStream.close();
                    } catch (IOException ex) {}
                }
            }
        });

    }

    @Override
    public void onClose(String token, WebSocketSession session, CloseStatus closeStatus) throws IOException {
        map.keySet().forEach(item -> {
            if (!item.startsWith(token)) {
                return;
            }
            TerminalSessionManager.Manager manager = map.get(item);
            manager.getSession().disconnect();
            manager.getChannelShell().disconnect();
            manager.destroyThread();
        });
    }

    @Override
    public Rep<List<ServerCompleteVO>> index(Req<RemotePartDTO, SettingUserVO> requestData) throws BasicException {
        RemotePartDTO data = requestData.getData();
        String token = data.getToken();
        String prefix = token.substring(0, token.indexOf(":"));
        SettingUserVO authUser = (SettingUserVO) easySecurityServer.getAuthUser(prefix);
        token = authUser.getId()+":"+authUser.getTenantId()+token.substring(token.indexOf(":"));
        WebSocketSession webSocketSession = SocketSessionManager.get(token);
        List<ServerCompleteVO> byIds = serverMapper.findByIds(data.getList());
        SocketMessageVO<String> socketMessageVO = new SocketMessageVO<String>();
        byIds.stream().map(i -> BeanUtil.copyProperties(i, SSHInfoDTO.class)).forEach(i -> {
            String id = data.getToken() + ":" + i.getId();
            SocketMessageVO sm = BeanUtil.copyProperties(socketMessageVO, SocketMessageVO.class);
            Thread thread = new Thread(() -> {
                sm.setType(id);
                try {
                    TerminalSessionManager.Manager manager = new TerminalSessionManager.Manager();
                    SSHMessageDTO sshMessageDTO = new SSHMessageDTO();
                    SSHInfoDTO sshInfoDTO = new SSHInfoDTO();
                    sshInfoDTO.setServerTimeout(i.getServerTimeout());
                    sshMessageDTO.setInfo(sshInfoDTO);
                    manager.setSshMessageDTO(sshMessageDTO);
                    manager.setThread(Thread.currentThread());
                    Session session = SSHManagerUtil.setSession(manager, i);
                    ChannelShell channelShell = SSHManagerUtil.setChannelShell(session, manager, data.getWindow());
                    map.put(id, manager);
                    sm.setObj("Connection successful,connect status 200 \r\n");
                    synchronized (webSocketSession){
                        webSocketSession.sendMessage(new TextMessage(JSONUtil.toJsonStr(sm)));
                    }
                    sendMessage(manager, channelShell, webSocketSession, id);
                } catch (Exception e) {
                    try {
                        log.error("{} 终端连接出错 {}", i.getServerName(), e.getMessage());
                        sm.setObj(e.getMessage()+",Connect status 500 \r\n");
                        synchronized (webSocketSession){
                            webSocketSession.sendMessage(new TextMessage(JSONUtil.toJsonStr(sm)));
                        }
                    } catch (IOException ex) {
                    }
                }
            });
            thread.setName("多联终端-"+i.getServerName());
            thread.start();
        });
        return Rep.ok(byIds);
    }

    final String root = "/";
    @Override
    public Rep<String> upload(String token,String p ,MultipartFile file) {
        try {
            if (file.isEmpty()) {
                return Rep.error(500,"上传失败，请选择文件");
            }
            if(StrUtil.isBlank(p)){
                p = root;
            }else {
                if(!root.equals(p.substring(p.length() - 1))){
                    p +=root;
                }
            }
            String finalP = p;
            map.keySet().forEach(item -> {
                InputStream inputStream = null;
                try {
                    inputStream = file.getInputStream();
                    if (!item.startsWith(token)) {
                        return;
                    }
                    TerminalSessionManager.Manager manager = map.get(item);
                    synchronized (manager) {
                        ChannelSftp channelSftp = SSHManagerUtil.setChannelSFTP(manager.getSession(), manager);
                        String pathFile = file.getOriginalFilename();
                        if (pathFile.lastIndexOf("/") != -1) {
                            String dir = pathFile.substring(0, pathFile.lastIndexOf("/"));
                            try {
                                // 校验文件是否存在，不存在会报错
                                channelSftp.ls(finalP+dir);
                            }catch (Exception e){
                                // 不存在则逐级创建
                                String[] split = dir.split("/");
                                String path = "";
                                for (int i = 0; i < split.length; i++) {
                                    path += split[i]+"/";
                                    try{
                                        channelSftp.mkdir(finalP+path);
                                    }catch (Exception ex){
                                        // 重复创建会报错
                                    }
                                }
                            }
                        }
                        channelSftp.put(inputStream, finalP+file.getOriginalFilename());
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Rep.ok();
    }

    private void del(ChannelSftp sftpChannel,String dir){
        try {
            // 尝试列出远程路径的内容
            Vector<?> files = sftpChannel.ls(dir);
            // 如果远程路径存在，删除它
            if (files != null && !files.isEmpty()) {
                // 删除目录和下的所有文件
                sftpChannel.rmdir(dir);
            }
        } catch (SftpException e) {
            // 远程路径不存在，忽略异常
        }
    }

    @Override
    public void packageCheck(PackagesBO.Item item,Integer tenantId) throws BasicException {
        if(!item.getOpen()){
            throw new BasicException(500,"当前套餐不支持此功能");
        }
    }
}
